#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <functional>
#include <memory>
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Socket.hpp"

using namespace socket_ns;
class TcpServer; // 声明

using http_t = std::function<std::string(std::string request)>;

class ThreadData
{
public:
  ThreadData(socket_sptr fd, InetAddr addr, TcpServer *s) : sockfd(fd), clientaddr(addr), self(s)
  {
  }

public:
  socket_sptr sockfd;
  InetAddr clientaddr;
  TcpServer *self;
};

class TcpServer
{
public:
  TcpServer(int port, http_t service)
      : _localaddr("0", port), _listensock(std::make_unique<TcpSocket>()), _http_service(service), _isrunning(false)
  {
    _listensock->BuildListenSocket(_localaddr);
  }

  static void *HandlerSock(void *args) // IO和业务进行解耦合
  {
    pthread_detach(pthread_self()); // 线程分离
    ThreadData *td = static_cast<ThreadData *>(args);
    // 需要调用Service函数，但是Service函数是类内函数，静态成员函数没有this指针无法调用，如何解决？
    // 将this指针设为ThreadData的类内成员，再通过这个this调用Service

    std::string request, response;

    // 不对请求做完整判断，大概率读取到的是完整报文
    ssize_t n = td->sockfd->Recv(&request); // 读取请求
    if (n > 0)
    {
      response = td->self->_http_service(request);
      td->sockfd->Send(response); // 发送响应
    }
    td->sockfd->Close();
    delete td;
    return nullptr;
  }
  void Loop()
  {
    _isrunning = true;
    // 4. 不能直接接收数据，先获取连接
    while (_isrunning)
    {
      InetAddr peeraddr;
      socket_sptr normalsock = _listensock->Accepter(&peeraddr);
      if (normalsock == nullptr)
        continue;

      // version 2 ：采用多线程
      pthread_t t;
      ThreadData *td = new ThreadData(normalsock, peeraddr, this);
      pthread_create(&t, nullptr, HandlerSock, td);
    }
    _isrunning = false;
  }
  ~TcpServer()
  {
  }

private:
  InetAddr _localaddr;
  std::unique_ptr<Socket> _listensock;
  bool _isrunning;

  http_t _http_service;
};